package my.springboot.redis;

import java.util.Collections;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

@Service
public class RedisDaoImpl implements RedisDao {

	private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
	
    private static final Long RELEASE_SUCCESS = 1L;
    
    private static final String UNLOCK_LUA = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end ";
    
//	@Autowired
	@Resource
	private RedisTemplate<String, ?> redisTemplate;

	/**
	 * 设置值
	 */
	@Override
	public boolean set(final String key, final String value) {
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.set(serializer.serialize(key), serializer.serialize(value));
				return true;
			}
		});
		return result;
	}
	
	public String get(final String key) {
		String result = redisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				byte[] value = connection.get(serializer.serialize(key));
				return serializer.deserialize(value);
			}
		});
		return result;
	}

	/**
	 * 加锁
	 * @param key 
	 * @param value
	 * @param expireTime
	 * @return
	 */
	public boolean lock(final String key, final String value,final int expireTime) {
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				//获取原生命令
				JedisCommands jedisCommands = (JedisCommands) connection.getNativeConnection();
				String result = jedisCommands.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
				if (LOCK_SUCCESS.equals(result)) {
		            return true;
		        }
				return false;
			}
		});
		return result;
	}


	/**
	 * 解锁
	 * @param key 
	 * @param value
	 * @return
	 */
	public boolean release(final String key, final String value) {
		
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
				connection.set(serializer.serialize(key), serializer.serialize(value));
				//获取原生命令
				Object nativeConnection = connection.getNativeConnection();
				// 集群模式和单机模式虽然执行脚本的方法一样，但是没有共同的接口，所以只能分开执行
                // 集群模式
                if (nativeConnection instanceof JedisCluster) {
                    return RELEASE_SUCCESS.equals((Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA,
                    		Collections.singletonList(key), Collections.singletonList(value)));
                }
                // 单机模式
                else if (nativeConnection instanceof Jedis) {
                    return RELEASE_SUCCESS.equals((Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, 
                    		Collections.singletonList(key), Collections.singletonList(value)));
                }
				return false;
			}
		});
		return result;
	}

	
	
	// @Override
	// public boolean expire(final String key, long expire) {
	// return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
	// }
	//
	// @Override
	// public <T> boolean setList(String key, List<T> list) {
	// String value = JsonUtil.objToJson(list);
	// return set(key, value);
	// }
	//
	// @Override
	// public <T> List<T> getList(String key, Class<T> clz) {
	// String json = get(key);
	// if (json != null) {
	// List<T> list = (List<T>) JsonUtil.jsonToObj(json, List.class);
	// return list;
	// }
	// return null;
	// }
	//
	// @Override
	// public long lpush(final String key, Object obj) {
	// final String value = JsonUtil.objToJson(obj);
	// long result = redisTemplate.execute(new RedisCallback<Long>() {
	// @Override
	// public Long doInRedis(RedisConnection connection) throws
	// DataAccessException {
	// RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
	// long count = connection.lPush(serializer.serialize(key),
	// serializer.serialize(value));
	// return count;
	// }
	// });
	// return result;
	// }
	//
	// @Override
	// public long rpush(final String key, Object obj) {
	// final String value = JsonUtil.objToJson(obj);
	// long result = redisTemplate.execute(new RedisCallback<Long>() {
	// @Override
	// public Long doInRedis(RedisConnection connection) throws
	// DataAccessException {
	// RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
	// long count = connection.rPush(serializer.serialize(key),
	// serializer.serialize(value));
	// return count;
	// }
	// });
	// return result;
	// }
	//
	// @Override
	// public String lpop(final String key) {
	// String result = redisTemplate.execute(new RedisCallback<String>() {
	// @Override
	// public String doInRedis(RedisConnection connection) throws
	// DataAccessException {
	// RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
	// byte[] res = connection.lPop(serializer.serialize(key));
	// return serializer.deserialize(res);
	// }
	// });
	// return result;
	// }

}
